Benjy's Blog

「All In One 主机」— 硬件直通:SATA 直通、GVT-g 双 VM 共享核显与网卡桥接

2025-06-15

按照最初的规划,PVE 主机上的硬件准备按需直通给内部的虚拟机,比如 SATA 控制器直通给群晖,AMD RX550 显卡直通给飞牛,Intel i226-v 网卡直通给 iKuai,还有 USB 接口等等直通。除了 SATA 和 网卡之外,显卡直通并不是最高优先级,所以放在了最后,没想到这一步花了我大半天的精力。最终结论是这个 Intel Q370 主板不支持交叉直通,这种定制的主板多少还是限制有点多,不如消费级主板来得实在,不过直通不行,虚拟化还是可以的,牺牲 3%-5% 的性能也能满足,简单记一下吧。


一、开启硬件直通

1
2
3
4
5
6
7
8
9
10
11
12
13
# 修改 GRUB 配置
nano /etc/default/grub

# 找到 GRUB_CMDLINE_LINUX_DEFAULT,并根据平台修改,我这里是AMD
# amd_iommu=on:开启 AMD IOMMU
# iommu=pt:让设备默认直通,减少内核占用
# video=efifb:off:避免内核抢占显卡帧缓冲
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt video=efifb:off"

#更新,并重启
update-grub
update-initramfs -u -k all
reboot

二、SATA 控制器直通

由于我硬盘里有群晖的数据,所以把 SATA 控制器直通给群晖是必须的操作,这样相当于我可以无缝换机。

首先在 PVE Shell 确认 SATA 控制器地址:

1
2
3
lspci | grep -i sata
# 800 G5 SFF 输出类似:
# 00:17.0 SATA controller: Intel Corporation Cannon Lake PCH SATA AHCI Controller

在 PVE Web → 群晖 VM → 硬件 → 添加 → PCI 设备,选择 00:17.0,勾选 All Functions,保存并重启群晖。
接着群晖内部就能看到之前的存储空间,S.M.A.R.T 正常,SATA 直通顺利完成。


三、AMD RX550 直通给飞牛

接下来把 RX550 直通给飞牛,先确认设备ID,这里会看到两个RX550的设备号,一个是显卡一个是声卡,两个一起直通就行。

1
2
3
4
lspci -nn | grep VGA
# 800 G5 SFF 输出类似:
# 01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Lexa PRO [Radeon RX 550/550X] [1002:699f] (rev ff)
# 01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] [1002:aae0]

在 PVE Web → 飞牛 VM → 硬件 → 添加 → PCI 设备,选择 01:00.0,勾选 All Functions,保存。
在 PVE Web → 飞牛 VM → 硬件 → 添加 → PCI 设备,选择 01:00.1,勾选 All Functions,保存。
保存完成重启飞牛,等飞牛启动完成,进入飞牛设置可以发现已经能正确识别到显卡,接着开启硬件加速和AI相册即可。

在我以为一切都很顺利的时候,问题来了。后台的群晖莫名其妙关闭了,直接点启动也启动不了。

第一轮排查:AMD GPU Reset 问题

AMD 独显直通有个出了名的老坑——GPU Reset Bug。RX 系列显卡在虚拟机关机或重启时,GPU 无法正确复位,导致宿主机上依赖该显卡的进程出现异常,严重时会影响同一宿主机上的其他 VM:

1
2
3
4
5
6
# 检查内核是否支持 vendor reset
lsmod | grep vendor_reset
# 若无输出,安装 vendor-reset 模块
apt install pve-headers-$(uname -r)
# 从源码编译安装 vendor-reset(AMD GPU reset 补丁)
# 配置自启

重启后,先开群晖,再开飞牛,群晖依旧闪退,排除该因素。

第二轮排查:IOMMU 分组问题

转而怀疑 IOMMU 分组划分有问题,SATA 控制器和 RX550 可能落在同一分组或存在依赖。

1
2
3
4
5
6
# 查看所有 IOMMU 分组
for d in /sys/kernel/iommu_groups/*/devices/*; do
group=$(echo $d | awk -F/ '{print $5}')
dev=$(basename $d)
echo "Group $group: $dev $(lspci -nns $dev | head -1)"
done | sort -t' ' -k2 -n

查出来两者确实在不同分组,理论上不应该互相干扰。尝试加上 pcie_acs_override=downstream,multifunction 内核参数强制拆分分组,重启后群晖还是闪退。
我有点怀疑是硬件问题,于是我把前面的设置全部回退,回到最开始的配置,开始验证是否是硬件问题。

第三轮排查:最小化复现

1、先是不直通显卡给飞牛,验证启动两个VM,结果启动成功,说明这个起点的状态是没问题的。
2、验证启动顺序问题,直通后,分别验证两者启动先后的场景,发现先开群晖再开飞牛, 群晖闪退。先开飞牛再开群晖,群晖启动失败,所以应该不是顺序原因。
3、验证硬件问题,这次不直通显卡,直通网卡给飞牛,依旧失效,说明大概率不是网卡和显卡的问题(两个卡在我别的电脑上正常使用)。
4、这次把显卡网卡全部直通给群晖,启动后,所有设备正常,群晖内部也能正确识别。
结合豆包的分析,大概确定了可能是主板的问题。这个主板一次只能直通一个VM,多设备分开直通时 DMA remapping 就会导致总线崩溃。

所以最初的方案应该不可行,只保留 SATA 直通给群晖,其他 VM 放弃直通使用软件虚拟化。


四、改方案:GVT-g 把核显虚拟化,同时分配给群晖和飞牛

既然独显直通无法和 SATA 直通共存,转而使用核显的 GVT-g(Intel Graphics Virtualization Technology - graphics)。正好我的 i5-9500 的 UHD 630 支持 GVT-g,这是 Coffee Lake 核显的能力。Intel 8/9 代酷睿的核显支持 SR-IOV 的前身——Mediated Device(mdev)虚拟化。宿主机保留核显控制权,同时虚拟出若干个 vGPU 实例,每个实例分配给不同 VM,VM 里看到的是一块完整的”显卡”。

4.1 宿主机启用 GVT-g

编辑 /etc/default/grub

1
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt i915.enable_gvt=1"

编辑 /etc/modules,追加以下四行:

1
2
3
4
kvmgt
vfio
vfio_iommu_type1
vfio_mdev

更新并重启:

1
2
3
update-grub
update-initramfs -u -k all
reboot

重启后确认 GVT-g 就绪:

1
2
3
ls /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/
# 应当看到:
# i915-GVTg_V5_1 i915-GVTg_V5_2 i915-GVTg_V5_4 i915-GVTg_V5_8

各类型的显存规格:

类型 显存 适合场景 最多实例数
V5_1 512 MB 3D 渲染、复杂任务 1
V5_2 256 MB 一般用途 2
V5_4 128 MB 照片编解码、视频转码 4
V5_8 64 MB 基础显示输出 8

群晖和飞牛都选 V5_4(128MB),各创建一个实例,两个 VM 同时享用核显加速。

4.2 创建两个 vGPU 实例并持久化

生成两个固定 UUID:

1
2
3
4
UUID_SYNOLOGY=$(uuidgen)
UUID_FNOS=$(uuidgen)
echo "群晖: $UUID_SYNOLOGY"
echo "飞牛: $UUID_FNOS"

记下这两个值。创建 systemd 服务,开机自动创建两个 mdev 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > /etc/systemd/system/gvt-g-vgpu.service << 'EOF'
[Unit]
Description=Create Intel GVT-g vGPU instances for Synology and fnOS
After=syslog.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c '\
echo <UUID_SYNOLOGY> > /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/create && \
echo <UUID_FNOS> > /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/create'
ExecStop=/bin/bash -c '\
echo 1 > /sys/bus/pci/devices/0000:00:02.0/<UUID_SYNOLOGY>/remove; \
echo 1 > /sys/bus/pci/devices/0000:00:02.0/<UUID_FNOS>/remove'

[Install]
WantedBy=multi-user.target
EOF

<UUID_SYNOLOGY><UUID_FNOS> 替换成刚才生成的实际 UUID,共 4 处。

1
2
3
systemctl enable --now gvt-g-vgpu.service
# 验证两个实例都创建成功
ls /sys/bus/pci/devices/0000:00:02.0/ | grep -E '[0-9a-f]{8}-'

应能看到两个 UUID 目录。

4.3 将 vGPU 挂给群晖 VM

编辑配置文件:

1
2
# 找到群晖 VMID,我的是 100
nano /etc/pve/qemu-server/100.conf

追加一行:

1
hostpci1: 0000:00:02.0,mdev=<UUID_SYNOLOGY>

重启群晖 VM 后识别成功

1
2
3
# SSH 进群晖
ls /dev/dri/
# 看到 renderD128 即 vGPU 识别成功

群晖中:套件中心 → Synology Photos → 设置 → 硬件加速 → 启用

4.4 将 vGPU 挂给飞牛 VM

同样步骤,飞牛 VM 的配置文件追加:

1
hostpci0: 0000:00:02.0,mdev=<UUID_FNOS>

重启飞牛 VM 后,在设备信息中看到 GPU 即代表成功:


五、网卡:Linux 虚拟网桥 vs PCIe 直通

有了前面的经验,直通网卡的方式显然是走不通了,不过好在使用虚拟网桥也很方便。

1
2
物理网卡 i226-v 口1 ──→ vmbr1 ──→ iKuai WAN(VirtIO 虚拟网卡)
物理网卡 i226-v 口2 ──→ vmbr2 ──→ iKuai LAN(VirtIO 虚拟网卡)

PVE 在 L2 层桥接物理网口,iKuai VM 通过 VirtIO 虚拟网卡接入桥接交换机,转发延迟几乎为零(VirtIO 的软件开销比 PCIe 直通略高,但对家用网络完全感知不到)。

配置网桥参数:

1
nano /etc/network/interfaces

增加以下配置

1
2
3
4
5
6
7
8
9
10
11
auto vmbr1
iface vmbr1 inet manual
bridge-ports enp1s0f0
bridge-stp off
bridge-fd 0

auto vmbr2
iface vmbr2 inet manual
bridge-ports enp1s0f1
bridge-stp off
bridge-fd 0

iKuai VM 硬件页面分别添加两块 VirtIO 网卡,桥接到 vmbr1(WAN)和 vmbr2(LAN),iKuai 内部正常配置上下行即可。


六、最终硬件直通方案汇总

设备 方案 受益 VM 效果
SATA 控制器(00:17.0) PCIe 直通 群晖 DSM 直接管理物理盘,S.M.A.R.T 完整
Intel UHD 630 vGPU-1 GVT-g(V5_4,128MB) 群晖 Photos Quick Sync 加速,人脸识别显著提速
Intel UHD 630 vGPU-2 GVT-g(V5_4,128MB) 飞牛 飞牛影音 QuickSync 硬解 H.264/H.265
AMD RX550 未直通,留给宿主机 PVE 接显示器,调试时直接看 PVE 控制台输出
Intel i226-v 双口网卡 Linux 网桥(vmbr1/vmbr2) iKuai 2.5G WAN/LAN,性能无损,宿主机保留控制

Tags: PVE